package com.shawn.server.service;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.shawn.autoconfig.template.OssTemplate;
import com.shawn.commons.utils.CommentType;
import com.shawn.commons.utils.RC;
import com.shawn.dubbo.api.*;
import com.shawn.model.entity.UserInfo;
import com.shawn.model.mongo.Movement;
import com.shawn.model.mongo.Visitors;
import com.shawn.model.vo.ErrorResult;
import com.shawn.model.vo.MovementsVo;
import com.shawn.model.vo.PageResult;
import com.shawn.model.vo.VisitorsVo;
import com.shawn.server.annotation.LogConfig;
import com.shawn.server.annotation.LogEnum;
import com.shawn.server.exception.BusinessException;
import com.shawn.server.interceptor.ThreadContext;
import com.shawn.server.util.CalculateDistance;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Service
public class MovementService {

    @DubboReference
    private MovementApi movementApi;

    @DubboReference
    private UserInfoApi userInfoApi;

    @DubboReference
    private CommentsApi commentsApi;

    @DubboReference
    private LocationApi locationApi;


    @DubboReference
    private VisitorsApi visitorsApi;

    //阿里云保存业务
    @Autowired
    private OssTemplate ossTemplate;

    /**
     * reids
     */
    @Autowired
    private RedisTemplate<String,String> redisTemplate;

    @Autowired
    private MqMessageService messageService;

    /**
     * 发布动态
     *
     * @param imageContent 图像内容
     * @param movement     运动
     * @throws IOException ioexception
     */
//    @LogConfig(objId = "#result",key = "movement",type = LogEnum.PUBLISH_DYNAMIC)
    public void publishDynamic(MultipartFile[] imageContent, Movement movement) throws IOException {
        //遍历文件集合,上传至阿里云
        List<String> list = new ArrayList<>();
        for (MultipartFile multipartFile : imageContent) {
            String url = ossTemplate.upLoading(Objects.requireNonNull(multipartFile.getOriginalFilename()), multipartFile.getInputStream());
            list.add(url);
        }
        movement.setMedias(list);

        //获取userId
        Long userId = ThreadContext.getUserId();
        movement.setUserId(userId);
        String id = movementApi.saveMovement(movement);
        //记录用户发布动态信息
        messageService.sendLogMessage(userId,"0201","movement",id);
        //发送审核消息
        messageService.sendAudiMessage(id);
    }

    /**
     * 得到自己所有动态
     *
     * @param page     页面
     * @param pageSize 页面大小
     * @param userId   用户id
     * @return {@link PageResult}<{@link MovementsVo}>
     */
    public PageResult<MovementsVo> getAllDynamics(Integer page, Integer pageSize, Long userId) {
        IPage<Movement> iPage = movementApi.selectByPage(page, pageSize, userId);
        if (iPage.getTotal()==0){
            return new PageResult<>();
        }
        //查询当前用户信息
        UserInfo userInfo = userInfoApi.findDetailById(userId);
        //获取动态信息
        List<Movement> movementList = iPage.getRecords();
        //获取当前坐标
        List<Double> coordinates = locationApi.getNowLocation(userId);
        Double lo1 = coordinates.get(0);
        Double la1 = coordinates.get(1);
        List<MovementsVo> movementsVos = new ArrayList<>();
        for (Movement movement : movementList) {
            MovementsVo movementsVo = MovementsVo.init(userInfo, movement);
            //计算动态发布地距当前位置距离
            //获取动态坐标
            String lo2 = movement.getLongitude();
            String la2 = movement.getLatitude();
            //计算距离
            String distance = CalculateDistance.getDistance(lo1, la1, lo2, la2);
            if(lo1==null) {
                movementsVo.setDistance("未知距离");
            }else {
                movementsVo.setDistance(distance);
            }
            //查询所有评论,查看当前用户是否已点赞,或喜欢
            //查询redis,判断当前用户是否已点赞
            String likeKey = RC.MOVEMENTS_INTERACT_KEY+movement.getId().toHexString();
            String likeHashKey = RC.MOVEMENT_LIKE_HASH_KEY+userId;
            Boolean hasLiked = redisTemplate.opsForHash().hasKey(likeKey, likeHashKey);
//            Boolean hasLiked = commentsApi.whetherLikeOrLove(userId, movement.getId().toHexString(), CommentType.LIKE);
//            Boolean hasLoved = commentsApi.whetherLikeOrLove(userId, movement.getId().toHexString(), CommentType.LOVE);
            movementsVo.setHasLiked(hasLiked?1:0);
            movementsVos.add(movementsVo);
        }
        //        messageService.sendLogMessage(userId,"0202","movement",null);
        return new PageResult<>(page, pageSize, (int) iPage.getTotal(), movementsVos);
    }

    /**
     * 得到朋友动态
     *order-by created
     * @param userId 用户id
     * @return {@link IPage}<{@link Movement}>
     */
    public PageResult<MovementsVo> getFriendsDynamics(Integer page,Integer pageSize,Long userId) {
        IPage<Movement> iPage = movementApi.getFriendsDynamics(page, pageSize, userId);
        if (iPage.getTotal()==0){
            return new PageResult<>();
        }
        //根据动态信息查询个人信息
        List<Movement> movements = iPage.getRecords();
        //        messageService.sendLogMessage(userId,"0202","movement",null);
        return getMovementsVoPageResult(page, pageSize, userId, (int) iPage.getTotal(), movements);
    }

    private PageResult<MovementsVo> getMovementsVoPageResult(Integer page, Integer pageSize, Long userId,Integer count, List<Movement> movements) {
        List<MovementsVo> movementsVos = new ArrayList<>();

        //获取当前坐标
        List<Double> coordinates = locationApi.getNowLocation(userId);
        Double lo1 = coordinates.get(0);
        Double la1 = coordinates.get(1);

        String likeHashKey = RC.MOVEMENT_LIKE_HASH_KEY+userId;
        String loveHashKey = RC.MOVEMENT_LOVE_HASH_KEY+userId;
        for (Movement movement : movements) {
            UserInfo userInfo = userInfoApi.findDetailById(movement.getUserId());
            MovementsVo movementsVo = MovementsVo.init(userInfo, movement);
            //计算动态发布地距当前位置距离
            //获取动态坐标
            String lo2 = movement.getLongitude();
            String la2 = movement.getLatitude();
            //计算距离
            String distance = CalculateDistance.getDistance(lo1, la1, lo2, la2);
            if(lo1==null) {
                movementsVo.setDistance("未知距离");
            }else {
                movementsVo.setDistance(distance);
            }
            //查询redis,判断当前用户是否已点赞
            String likeKey = RC.MOVEMENTS_INTERACT_KEY+movement.getId().toHexString();

            Boolean hasLiked = redisTemplate.opsForHash().hasKey(likeKey, likeHashKey);

            //查询redis,判断当前用户是否已喜欢
            String loveKey = RC.MOVEMENTS_INTERACT_KEY+movement.getId().toHexString();
            Boolean hasLoved = redisTemplate.opsForHash().hasKey(loveKey, loveHashKey);
            //查询所有评论,查看当前用户是否已点赞,或喜欢
//            Boolean hasLike = commentsApi.whetherLikeOrLove(userId, movement.getId().toHexString(), CommentType.LIKE);
//            Boolean hasLove = commentsApi.whetherLikeOrLove(userId, movement.getId().toHexString(), CommentType.LOVE);
            movementsVo.setHasLiked(hasLiked?1:0);
            movementsVo.setHasLoved(hasLoved?1:0);
            movementsVos.add(movementsVo);
        }
        return new PageResult<>(page, pageSize,count, movementsVos);
    }

    /**
     * 获取推荐动态内容
     *
     * @param page     页面
     * @param pageSize 页面大小
     * @return {@link PageResult}<{@link MovementsVo}>
     */
    public PageResult<MovementsVo> getRecommendDynamics(Integer page, Integer pageSize) {
        Long userId = ThreadContext.getUserId();
        int count =0;
        //从redis获取该用户推荐的pid
        String pidKey = RC.MOVEMENTS_RECOMMEND+userId;
        String pid = redisTemplate.opsForValue().get(pidKey);
        List<Movement> list = new ArrayList<>();
        if(Strings.isEmpty(pid)){
            //随机抽取{pageSize}条数据 不包含自己
            list = movementApi.getRandomMovement(pageSize,userId);
            count = list.size();
        }else {
            //如果不为空,查询动态
            String[] pids = pid.split(",");
            //判断当前页起始条数是否小于数据长度
            List<Long> pidList = null;
            if ((page - 1) * pageSize < pids.length) {
                pidList = Arrays.stream(pids)
                        //跳过当前页码数量推荐id
                        .skip((long) (page - 1) * pageSize)
                        //限制获取数量
                        .limit(pageSize)
                        //转成Long类型
                        .map(Long::valueOf)
                        //用集合接收
                        .collect(Collectors.toList());
            }else {
                pidList = Arrays.stream(pids).map(Long::valueOf).collect(Collectors.toList());
            }
            list = movementApi.getByIdList(pidList, userId);
            count = list.size();

        }
        return getMovementsVoPageResult(page, pageSize, userId, count, list);
    }

    //获取单条动态方法
    public MovementsVo getSingleDynamic(String id) {
        Movement movement = movementApi.getById(id);
        UserInfo userInfo = userInfoApi.findDetailById(movement.getUserId());
        MovementsVo movementsVo = MovementsVo.init(userInfo, movement);
        messageService.sendLogMessage(userInfo.getId(),"0202","movement",movement.getId().toHexString());
        return movementsVo;
    }

    //点赞或取消点赞方法
    public Integer likeOrDislike(String id,Number number) {
        Long userId = ThreadContext.getUserId();
        //判断该用户是否已点赞
        boolean likedRes = commentsApi.whetherLikeOrLove(userId,id,CommentType.LIKE);
        //如果用户是 重复点赞||未点赞而取消赞
        if(number.equals(1)&&likedRes){
            throw new BusinessException(ErrorResult.likeError());
        }else if(number.equals(-1)&&!likedRes) {
            throw new BusinessException(ErrorResult.disLikeError());
        }
        Integer likeCount = movementApi.operateLikeOrLove(id, userId,CommentType.LIKE, number);
        //点赞信息存入redis,取消赞删除redis
        String likeKey = RC.MOVEMENTS_INTERACT_KEY+id;
        String userHashKey = RC.MOVEMENT_LIKE_HASH_KEY+userId;
        if(number.equals(1)){
            redisTemplate.opsForHash().put(likeKey,userHashKey,"1");
        }else {
            redisTemplate.opsForHash().delete(likeKey,userHashKey);
        }
        messageService.sendLogMessage(userId,number.equals(1)?"0203":"0206","movement",id);
        return likeCount;
    }

    //喜欢或取消喜欢方法
    public Integer loveOrNotLove(String id, Number number) {
        Long userId = ThreadContext.getUserId();
        //判断该用户是否已喜欢
        boolean lovedRes = commentsApi.whetherLikeOrLove(userId,id,CommentType.LOVE);
        //如果用户是 重复喜欢||未喜欢而取消喜欢
        if((number.equals(1)&&lovedRes)||(number.equals(-1)&&!lovedRes)){
            throw new BusinessException(ErrorResult.disLoveError());
        }
        Integer loveCount = movementApi.operateLikeOrLove(id, userId, CommentType.LOVE, number);
        //点赞信息存入redis,取消赞删除redis
        String loveKey = RC.MOVEMENTS_INTERACT_KEY+id;
        String userHashKey = RC.MOVEMENT_LOVE_HASH_KEY+userId;
        if(number.equals(1)){
            redisTemplate.opsForHash().put(loveKey,userHashKey,"1");
        }else {
            redisTemplate.opsForHash().delete(loveKey,userHashKey);
        }
        messageService.sendLogMessage(userId,number.equals(1)?"0204":"0207","movement",id);
        return loveCount;
    }

    /**
     * 查看最近访客
     *
     * @param userId 用户id
     * @return {@link List}<{@link VisitorsVo}>
     */
    public List<VisitorsVo> getVisitors(Long userId) {
        //获取上次查看时间
        String key = RC.VISITORS_USER;
        String hashKey = String.valueOf(userId);
        String date = (String) redisTemplate.opsForHash().get(key, hashKey);
        long lookTime= 0L;
        if (date!=null) {
             lookTime = Long.parseLong(date);
        }
        //获取所有访客
        List<Visitors> visitors = visitorsApi.getVisitors(userId,lookTime);
        if (visitors.size()==0){
            return new ArrayList<>();
        }
        List<Long> ids = visitors.stream().map(Visitors::getUserId).collect(Collectors.toList());
        Map<Long, Visitors> visitorsMap = CollUtil.fieldValueMap(visitors, "userId");
        List<UserInfo> userInfos = userInfoApi.findByIds(ids);
        ArrayList<VisitorsVo> visitorVos = new ArrayList<>();
        for (UserInfo userInfo : userInfos) {
            VisitorsVo vo = VisitorsVo.init(userInfo, visitorsMap.get(userInfo.getId()));
            visitorVos.add(vo);
        }
        //设置本次查看时间
        redisTemplate.opsForHash().put(key,hashKey,String.valueOf(System.currentTimeMillis()));
        return visitorVos;
    }
}
